package com.github.florent37.mylittlecanvas.touch;

import com.github.florent37.mylittlecanvas.listeners.ClickedListener;
import com.github.florent37.mylittlecanvas.listeners.InvalidateListener;
import com.github.florent37.mylittlecanvas.listeners.ViewInvalidateListener;
import com.github.florent37.mylittlecanvas.shape.Shape;
import com.github.florent37.mylittlecanvas.touch.listeners.UpListener;
import com.github.florent37.mylittlecanvas.touch.scrollable.Scrollable;
import com.github.florent37.mylittlecanvas.touch.scrollable.ScrollableViewGroup;
import ohos.agp.components.*;
import ohos.multimodalinput.event.TouchEvent;

import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

public class ShapeEventManager {
    private final Reference<Component> viewReference;
    private final InvalidateListener invalidateListener;
    private final List<EventHelper> touchHelpers = new ArrayList<>();
    private boolean disableParentScrollOnTouch = true;
    private Scrollable parentScroll; //nonNull when searchedForParent
    private Component.TouchEventListener onTouchListener = new Component.TouchEventListener() {
        private TouchEvent lastEvent;

        @Override
        public boolean onTouchEvent(Component component, TouchEvent event) {
            if (parentScroll == null) {
                parentScroll = findParentScroll();
            }
            assert (parentScroll != null);
            switch (event.getAction()) {
                case TouchEvent.PRIMARY_POINT_DOWN:
                    if (isDisableParentScrollOnTouch()) {
                        parentScroll.disableScroll();
                    }
                    for (EventHelper touchHelper : touchHelpers) {
                        touchHelper.handleDown(lastEvent, event, component);
                    }
                case TouchEvent.POINT_MOVE:
                    for (EventHelper touchHelper : touchHelpers) {
                        touchHelper.handleMove(lastEvent, event, component);
                    }
                    this.lastEvent = event;
                    return true;
                case TouchEvent.CANCEL:
                case TouchEvent.PRIMARY_POINT_UP:
                    if (isDisableParentScrollOnTouch()) {
                        parentScroll.enableScroll();
                    }
                    for (EventHelper touchHelper : touchHelpers) {
                        touchHelper.handleUp(lastEvent, event, component);
                    }
            }
            this.lastEvent = event;
            return true;
        }


    };

    public boolean isDisableParentScrollOnTouch() {
        return disableParentScrollOnTouch;
    }

    public ShapeEventManager disableParentScrollOnTouch(boolean disableParentScrollOnTouch) {
        this.disableParentScrollOnTouch = disableParentScrollOnTouch;
        return this;
    }

    public ShapeEventManager(final Component view) {
        viewReference = new WeakReference<>(view);
        view.setTouchEventListener(onTouchListener);
        invalidateListener = new ViewInvalidateListener(view);
    }

    public ShapeEventManager ifTouched(final Shape shape, final TouchSetup touchSetup) {
        if (shape != null && touchSetup != null) {
            final EventHelper touchHelper = new EventHelper(invalidateListener, EventHelper.Event.TOUCH, shape);
            touchHelpers.add(touchHelper);
            touchSetup.setupTouch(touchHelper);
        }
        return this;
    }

    public <S extends Shape> ShapeEventManager ifClicked(final S shape, final ClickedListener<S> clickedListener) {
        if (shape != null && clickedListener != null) {
            final EventHelper touchHelper = new EventHelper(invalidateListener, EventHelper.Event.CLICK, shape);
            touchHelper.onUp(new UpListener() {
                @Override
                public void onUp(TouchEvent event) {
                    clickedListener.onClick(shape);
                }
            });
            touchHelpers.add(touchHelper);
        }
        return this;
    }

    @Deprecated
    public ShapeEventManager ifDoubleClicked(final Shape shape, final TouchSetup touchSetup) {
        if (shape != null && touchSetup != null) {
            final EventHelper touchHelper = new EventHelper(invalidateListener, EventHelper.Event.DOUBLE_CLICK, shape);
            touchHelpers.add(touchHelper);
            touchSetup.setupTouch(touchHelper);
        }
        return this;
    }

    public ShapeEventManager onTouchAywhere(final TouchSetup touchSetup) {
        if (touchSetup != null) {
            final EventHelper touchHelper = new EventHelper(invalidateListener);
            touchHelpers.add(touchHelper);
            touchSetup.setupTouch(touchHelper);
        }
        return this;
    }

    private Scrollable findParentScroll() {
        final Component view = viewReference.get();
        Scrollable scrollable = null;
        if (view != null) {
            final ComponentParent parent = view.getComponentParent();
            if (parent instanceof ComponentContainer) {
                scrollable = findParentScroll(((ComponentContainer) parent));
            }
        }
        if (scrollable == null) {
            scrollable = new ScrollableViewGroup(null);
        }
        return scrollable;
    }

    /**
     * If another Scroll container exists, add it into this switch
     *
     * @param view the component container
     * @return Scrollable
     */
    private Scrollable findParentScroll(ComponentContainer view) {
        if (view instanceof ScrollView) {
            return new ScrollableViewGroup(view);
        } else if (view instanceof NestedScrollView) {
            return new ScrollableViewGroup(view);
        } else if (view instanceof ListContainer) {
            return new ScrollableViewGroup(view);
        }
        final ComponentParent parent =  view.getComponentParent();
        if (parent instanceof ComponentContainer) {
            return findParentScroll((ComponentContainer) parent);
        } else { // root view
            return null;
        }
    }

    public interface TouchSetup {
        void setupTouch(EventHelper eventHelper);
    }
}
